package mornd.test5;

import lombok.Data;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.*;

import java.io.InputStream;
import java.time.ZoneId;
import java.util.Collections;
import java.util.TimeZone;

/**
 * @author: mornd
 * @dateTime: 2023/5/8 - 13:25
 * 模拟解析参数流程
 */
public class Demo {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);

        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setMethod("GET");
        request.setParameter("name", "tom");

        ServletInvocableHandlerMethod handlerMethod =
                new ServletInvocableHandlerMethod(new Controller1(), Controller1.class.getMethod("test", User.class, HttpMethod.class, ZoneId.class, TimeZone.class, InputStream.class));

        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
        // 添加 bind 工厂
        handlerMethod.setDataBinderFactory(factory);
        // 添加参数名解析器
        handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
        // 添加 参数解析器集合
        handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));

        ModelAndViewContainer container = new ModelAndViewContainer();

        // 执行目标方法
        handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);
        System.out.println("container.getModel() = " + container.getModel());
    }

    /**
     * 工具方法，添加常用的参数解析器
     * @param context
     */
    public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 参数解析组合器，依次执行，如果前面的解析器满足条件则break，不再往下找解析器
        HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
        // 解析携带了 @RequestParam 注解
        composite.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false));
        // restful 方式传参
        composite.addResolver(new PathVariableMethodArgumentResolver());
        // 解析 @RequestHeader
        composite.addResolver(new RequestHeaderMethodArgumentResolver(beanFactory));
        // 解析 @CookieValue
        composite.addResolver(new ServletCookieValueMethodArgumentResolver(beanFactory));
        // 解析 @Value 注解获取环境信息
        composite.addResolver(new ExpressionValueMethodArgumentResolver(beanFactory));
        // 解析方法中使用 HttpServletRequest 等参数
        composite.addResolver(new ServletRequestMethodArgumentResolver());
        // 解析方法上标注 @ModelAttribute
        composite.addResolver(new ServletModelAttributeMethodProcessor(false));
        // 解析 @RequestBody
        composite.addResolver(new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())));
        // 解析方法上没有标注 @ModelAttribute
        composite.addResolver(new ServletModelAttributeMethodProcessor(true));
        // 解析没有携带了 @RequestParam 注解
        composite.addResolver(new RequestParamMethodArgumentResolver(beanFactory, true));
        return composite;
    }

    @Configuration
    static class WebConfig {
    }

    @Controller
    static class Controller1 {
        @ResponseStatus(HttpStatus.OK)
        public ModelAndView test(@ModelAttribute User user,
                                 HttpMethod httpMethod, ZoneId zoneId, TimeZone timeZone, InputStream inputStream) {
            System.out.println("test->" + user);
            return null;
        }
    }

    @Data
    static class User {
        private String name;
    }
}
